package com.opennews.openplatform.myspringbootcore.controller

import com.opennews.openplatform.myspringbootcore.dto.ApiResponse
import com.opennews.openplatform.myspringbootcore.exception.EntityNotFoundException
import com.opennews.openplatform.myspringbootcore.util.getErrorResponse
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.http.converter.HttpMessageNotReadableException
import org.springframework.web.HttpRequestMethodNotSupportedException
import org.springframework.web.bind.MethodArgumentNotValidException
import org.springframework.web.bind.MissingServletRequestParameterException
import org.springframework.web.bind.annotation.ControllerAdvice
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.servlet.resource.NoResourceFoundException
import java.io.IOException

@ControllerAdvice
open class GeneralGlobalExceptionHandler {
    @ExceptionHandler(HttpRequestMethodNotSupportedException::class)
    fun handleHttpRequestMethodNotSupportedException(ex: HttpRequestMethodNotSupportedException): ResponseEntity<ApiResponse<Map<String, String>>> {
        return getErrorResponse(
            HttpStatus.METHOD_NOT_ALLOWED, mapOf(
                "message" to "Unsupported HTTP method: ${ex.method}"
            )
        )
    }

    @ExceptionHandler(MethodArgumentNotValidException::class)
    fun handleValidationExceptions(ex: MethodArgumentNotValidException): ResponseEntity<ApiResponse<Map<String, String>>> {
        val errors = mutableMapOf<String, String>()

        // Collects validation error messages from all invalid fields.
        ex.bindingResult.fieldErrors.forEach { error ->
            errors[error.field] = error.defaultMessage ?: "Unknown error"
        }

        return getErrorResponse(HttpStatus.BAD_REQUEST, errors)
    }

    @ExceptionHandler(MissingServletRequestParameterException::class)
    fun handleMissingServletRequestParameterException(ex: MissingServletRequestParameterException): ResponseEntity<ApiResponse<Map<String, String>>> {
        return getErrorResponse(
            HttpStatus.BAD_REQUEST, mapOf(
                "message" to "Missing required parameter: ${ex.parameterName}"
            )
        )
    }

    @ExceptionHandler(EntityNotFoundException::class)
    fun handleEntityNotFoundException(ex: EntityNotFoundException): ResponseEntity<ApiResponse<Map<String, String>>> {
        return getErrorResponse(
            HttpStatus.NOT_FOUND, mapOf(
                "message" to ex.message.orEmpty()
            )
        )
    }

    @ExceptionHandler(NoResourceFoundException::class)
    fun handleNoResourceFoundException(ex: NoResourceFoundException): ResponseEntity<ApiResponse<Map<String, String>>> {
        return getErrorResponse(
            HttpStatus.NOT_FOUND, mapOf(
                "message" to ex.message.orEmpty()
            )
        )
    }

    @ExceptionHandler(HttpMessageNotReadableException::class)
    fun handleHttpMessageNotReadableException(): ResponseEntity<ApiResponse<Map<String, String>>> {
        return getErrorResponse(
            HttpStatus.BAD_REQUEST, mapOf(
                "message" to "Invalid request body."
            )
        )
    }

    @ExceptionHandler(IOException::class)
    fun handleIOException(): ResponseEntity<ApiResponse<Map<String, String>>> {
        return getErrorResponse(
            HttpStatus.INTERNAL_SERVER_ERROR, mapOf(
                "message" to "File IO error"
            )
        )
    }

    @ExceptionHandler(Exception::class)
    fun handleGenericException(ex: Exception): ResponseEntity<ApiResponse<Map<String, String>>> {
        println(ex.message)

        return getErrorResponse(
            HttpStatus.INTERNAL_SERVER_ERROR, mapOf(
                "message" to "An unexpected error occurred"
            )
        )
    }
}